/*
 * @Author: your name
 * @Date: 2021-02-16 19:58:58
 * @LastEditTime: 2021-02-16 20:08:50
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: \demo_2021021601\include\common\my_vsprintf.c
 */

#include "bsp.h"
#include "ctype.h"
#include "div64.h"
#include "my_string.h"
#include "my_vsprintf.h"

#define unlikely(x) __builtin_expect(!!(x), 0)

#define ADDCH(str, ch) \
  do                   \
  {                    \
    if ((str) < end)   \
      *(str) = (ch);   \
    ++str;             \
  } while (0)

/**
 * simple_strtoul - convert a string to an unsigned long
 * @cp: The start of the string
 * @endp: A pointer to the end of the parsed string will be placed here
 * @base: The number base to use
 */
unsigned long simple_strtoul(const char *cp, char **endp, unsigned int base)
{
  unsigned long result = 0, value;

  if (!base)
  {
    base = 10;
    if (*cp == '0')
    {
      base = 8;
      cp++;
      if ((toupper(*cp) == 'X') && isxdigit(cp[1]))
      {
        cp++;
        base = 16;
      }
    }
  }
  else if (base == 16)
  {
    if (cp[0] == '0' && toupper(cp[1]) == 'X')
      cp += 2;
  }
  while (isxdigit(*cp) &&
         (value = isdigit(*cp) ? *cp - '0' : toupper(*cp) - 'A' + 10) < base)
  {
    result = result * base + value;
    cp++;
  }
  if (endp)
    *endp = (char *)cp;
  return result;
}

//EXPORT_SYMBOL(simple_strtoul);

/**
 * simple_strtol - convert a string to a signed long
 * @cp: The start of the string
 * @endp: A pointer to the end of the parsed string will be placed here
 * @base: The number base to use
 */
long simple_strtol(const char *cp, char **endp, unsigned int base)
{
  if (*cp == '-')
    return -simple_strtoul(cp + 1, endp, base);
  return simple_strtoul(cp, endp, base);
}

//EXPORT_SYMBOL(simple_strtol);

/**
 * simple_strtoull - convert a string to an unsigned long long
 * @cp: The start of the string
 * @endp: A pointer to the end of the parsed string will be placed here
 * @base: The number base to use
 */
unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base)
{
  unsigned long long result = 0, value;

  if (!base)
  {
    base = 10;
    if (*cp == '0')
    {
      base = 8;
      cp++;
      if ((toupper(*cp) == 'X') && isxdigit(cp[1]))
      {
        cp++;
        base = 16;
      }
    }
  }
  else if (base == 16)
  {
    if (cp[0] == '0' && toupper(cp[1]) == 'X')
      cp += 2;
  }
  while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp - '0' : (islower(*cp) ? toupper(*cp) : *cp) - 'A' + 10) < base)
  {
    result = result * base + value;
    cp++;
  }
  if (endp)
    *endp = (char *)cp;
  return result;
}

//EXPORT_SYMBOL(simple_strtoull);

/**
 * simple_strtoll - convert a string to a signed long long
 * @cp: The start of the string
 * @endp: A pointer to the end of the parsed string will be placed here
 * @base: The number base to use
 */
long long simple_strtoll(const char *cp, char **endp, unsigned int base)
{
  if (*cp == '-')
    return -simple_strtoull(cp + 1, endp, base);
  return simple_strtoull(cp, endp, base);
}

static int skip_atoi(const char **s)
{
  int i = 0;

  while (isdigit(**s))
    i = i * 10 + *((*s)++) - '0';
  return i;
}

#define ZEROPAD 1  /* pad with zero */
#define SIGN 2     /* unsigned/signed long */
#define PLUS 4     /* show plus */
#define SPACE 8    /* space if plus */
#define LEFT 16    /* left justified */
#define SPECIAL 32 /* 0x */
#define LARGE 64   /* use 'ABCDEF' instead of 'abcdef' */

#define SMALL 32 /* Must be 32 == 0x20 */

//static char * number(char * buf, char * end, unsigned long long num, int base, int size, int precision, int type)
static char *put_dec_trunc(char *buf, unsigned q)
{
  unsigned d3, d2, d1, d0;
  d1 = (q >> 4) & 0xf;
  d2 = (q >> 8) & 0xf;
  d3 = (q >> 12);

  d0 = 6 * (d3 + d2 + d1) + (q & 0xf);
  q = (d0 * 0xcd) >> 11;
  d0 = d0 - 10 * q;
  *buf++ = d0 + '0'; /* least significant digit */
  d1 = q + 9 * d3 + 5 * d2 + d1;
  if (d1 != 0)
  {
    q = (d1 * 0xcd) >> 11;
    d1 = d1 - 10 * q;
    *buf++ = d1 + '0'; /* next digit */

    d2 = q + 2 * d2;
    if ((d2 != 0) || (d3 != 0))
    {
      q = (d2 * 0xd) >> 7;
      d2 = d2 - 10 * q;
      *buf++ = d2 + '0'; /* next digit */

      d3 = q + 4 * d3;
      if (d3 != 0)
      {
        q = (d3 * 0xcd) >> 11;
        d3 = d3 - 10 * q;
        *buf++ = d3 + '0'; /* next digit */
        if (q != 0)
          *buf++ = q + '0'; /* most sign. digit */
      }
    }
  }
  return buf;
}

/* Same with if's removed. Always emits five digits */
static char *put_dec_full(char *buf, unsigned q)
{
  /* BTW, if q is in [0,9999], 8-bit ints will be enough, */
  /* but anyway, gcc produces better code with full-sized ints */
  unsigned d3, d2, d1, d0;
  d1 = (q >> 4) & 0xf;
  d2 = (q >> 8) & 0xf;
  d3 = (q >> 12);

  /*
	 * Possible ways to approx. divide by 10
	 * gcc -O2 replaces multiply with shifts and adds
	 * (x * 0xcd) >> 11: 11001101 - shorter code than * 0x67 (on i386)
	 * (x * 0x67) >> 10:  1100111
	 * (x * 0x34) >> 9:    110100 - same
	 * (x * 0x1a) >> 8:     11010 - same
	 * (x * 0x0d) >> 7:      1101 - same, shortest code (on i386)
	 */

  d0 = 6 * (d3 + d2 + d1) + (q & 0xf);
  q = (d0 * 0xcd) >> 11;
  d0 = d0 - 10 * q;
  *buf++ = d0 + '0';
  d1 = q + 9 * d3 + 5 * d2 + d1;
  q = (d1 * 0xcd) >> 11;
  d1 = d1 - 10 * q;
  *buf++ = d1 + '0';

  d2 = q + 2 * d2;
  q = (d2 * 0xd) >> 7;
  d2 = d2 - 10 * q;
  *buf++ = d2 + '0';

  d3 = q + 4 * d3;
  q = (d3 * 0xcd) >> 11; /* - shorter code */
  /* q = (d3 * 0x67) >> 10; - would also work */
  d3 = d3 - 10 * q;
  *buf++ = d3 + '0';
  *buf++ = q + '0';
  return buf;
}

/* No inlining helps gcc to use registers better */
static char *put_dec(char *buf, uint64_t num)
{
  while (1)
  {
    unsigned rem;
    if (num < 100000)
      return put_dec_trunc(buf, num);
    rem = do_div(num, 100000);
    buf = put_dec_full(buf, rem);
  }
}

static char *number(char *buf, char *end, signed long long num, int base, int size, int precision, int type)
{
  /* we are called with base 8, 10 or 16, only, thus don't need "G..."  */
  static const char digits[16] = "0123456789ABCDEF";

  char tmp[100];
  char sign;
  char locase;
  int need_pfx = ((type & SPECIAL) && base != 10);
  int i;

  /* locase = 0 or 0x20. ORing digits or letters with 'locase'
	 * produces same digits or (maybe lowercased) letters */
  locase = (type & SMALL);
  if (type & LEFT)
    type &= ~ZEROPAD;
  sign = 0;

  if (type & SIGN)
  {
    if (num < 0)
    {
      sign = '-';
      num = -num;
      size--;
    }
    else if (type & PLUS)
    {
      sign = '+';
      size--;
    }
    else if (type & SPACE)
    {
      sign = ' ';
      size--;
    }
  }
  if (need_pfx)
  {
    size--;
    if (base == 16)
      size--;
  }

  /* generate full string in tmp[], in reverse order */
  i = 0;
  if (num == 0)
    tmp[i++] = '0';
  /* Generic code, for any base:
	else do {
		tmp[i++] = (digits[do_div(num,base)] | locase);
	} while (num != 0);
	*/
  else if (base != 10)
  { /* 8 or 16 */
    int mask = base - 1;
    int shift = 3;

    if (base == 16)
      shift = 4;

    do
    {
      tmp[i++] = (digits[((unsigned char)num) & mask] | locase);
      num >>= shift;
    } while (num);
  }
  else
  { /* base 10 */
    i = (char *)put_dec(tmp, num) - tmp;
  }
  /* printing 100 using %2d gives "100", not "00" */
  if (i > precision)
    precision = i;
  /* leading space padding */
  size -= precision;
  if (!(type & (ZEROPAD + LEFT)))
  {
    while (--size >= 0)
      ADDCH(buf, ' ');
  }
  /* sign */
  if (sign)
    ADDCH(buf, sign);
  /* "0x" / "0" prefix */
  if (need_pfx)
  {
    ADDCH(buf, '0');
    if (base == 16)
      ADDCH(buf, 'X' | locase);
  }
  /* zero or space padding */
  if (!(type & LEFT))
  {
    char c = (type & ZEROPAD) ? '0' : ' ';

    while (--size >= 0)
      ADDCH(buf, c);
  }
  /* hmm even more zero padding? */
  while (i <= --precision)
    ADDCH(buf, '0');
  /* actual digits of result */
  while (--i >= 0)
    ADDCH(buf, tmp[i]);
  /* trailing space padding */
  while (--size >= 0)
    ADDCH(buf, ' ');
  return buf;
}

#if 0
static char * number(char * buf, char * end, unsigned long long num, int base, int size, int precision, int type)
{
	char c,sign,tmp[66];
	const char *digits;
	static const char small_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
	static const char large_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	int i;

	puts("digits\r\n");
	digits = (type & LARGE) ? large_digits : small_digits;
	if (type & LEFT)
		type &= ~ZEROPAD;
	if (base < 2 || base > 36)
		return NULL;
	c = (type & ZEROPAD) ? '0' : ' ';
	sign = 0;
	if (type & SIGN) {
		if ((signed long long) num < 0) {
			sign = '-';
			num = - (signed long long) num;
			size--;
		} else if (type & PLUS) {
			sign = '+';
			size--;
		} else if (type & SPACE) {
			sign = ' ';
			size--;
		}
	}
	if (type & SPECIAL) {
		if (base == 16)
			size -= 2;
		else if (base == 8)
			size--;
	}
	i = 0;
	if (num == 0)
		tmp[i++]='0';
	else while (num != 0)
		tmp[i++] = digits[do_div(num,base)];
	if (i > precision)
		precision = i;
	size -= precision;
	if (!(type&(ZEROPAD+LEFT))) {
		while(size-->0) {
			if (buf <= end)
				*buf = ' ';
			++buf;
		}
	}
	if (sign) {
		if (buf <= end)
			*buf = sign;
		++buf;
	}
	if (type & SPECIAL) {
		if (base==8) {
			if (buf <= end)
				*buf = '0';
			++buf;
		} else if (base==16) {
			if (buf <= end)
				*buf = '0';
			++buf;
			if (buf <= end)
				*buf = digits[33];
			++buf;
		}
	}
	puts("type&LEFT\r\n");
	if (!(type & LEFT)) {
		while (size-- > 0) {
			if (buf <= end)
				*buf = c;
			++buf;
		}
	}
	while (i < precision--) {
		if (buf <= end)
			*buf = '0';
		++buf;
	}
	while (i-- > 0) {
		if (buf <= end)
			*buf = tmp[i];
		++buf;
	}
	while (size-- > 0) {
		if (buf <= end)
			*buf = ' ';
		++buf;
	}
	return buf;
}
#endif
/**
 * my_vsnprintf - Format a string and place it in a buffer
 * @buf: The buffer to place the result into
 * @size: The size of the buffer, including the trailing null space
 * @fmt: The format string to use
 * @args: Arguments for the format string
 *
 * The return value is the number of characters which would
 * be generated for the given input, excluding the trailing
 * '\0', as per ISO C99. If you want to have the exact
 * number of characters written into @buf as return value
 * (not including the trailing '\0'), use vscnprintf. If the
 * return is greater than or equal to @size, the resulting
 * string is truncated.
 *
 * Call this function if you are already dealing with a my_va_list.
 * You probably want snprintf instead.
 */
int my_vsnprintf(char *buf, size_t size, const char *fmt, my_va_list args)
{
  int len;
  unsigned long long num;
  int i, base;
  char *str, *end, c;
  const char *s;

  int flags; /* flags to number() */

  int field_width; /* width of output field */
  int precision;   /* min. # of digits for integers; max
				   number of chars for from string */
  int qualifier;   /* 'h', 'l', or 'L' for integer fields */
                   /* 'z' support added 23/7/1999 S.H.    */
                   /* 'z' changed to 'Z' --davidm 1/25/99 */

  /* Reject out-of-range values early */
  if (unlikely((int)size < 0))
  {
    /* There can be only one.. */
    //		static int warn = 1;
    //		WARN_ON(warn);
    //		warn = 0;
    return 0;
  }

  str = buf;
  end = buf + size - 1;

  if (end < buf - 1)
  {
    end = ((void *)-1);
    size = end - buf + 1;
  }

  for (; *fmt; ++fmt)
  {
    if (*fmt != '%')
    {
      if (str <= end)
        *str = *fmt;
      ++str;
      continue;
    }

    /* process flags */
    flags = 0;
  repeat:
    ++fmt; /* this also skips first '%' */
    switch (*fmt)
    {
    case '-':
      flags |= LEFT;
      goto repeat;
    case '+':
      flags |= PLUS;
      goto repeat;
    case ' ':
      flags |= SPACE;
      goto repeat;
    case '#':
      flags |= SPECIAL;
      goto repeat;
    case '0':
      flags |= ZEROPAD;
      goto repeat;
    }
    /* get field width */
    field_width = -1;
    if (isdigit(*fmt))
      field_width = skip_atoi(&fmt);
    else if (*fmt == '*')
    {
      ++fmt;
      /* it's the next argument */
      field_width = va_arg(args, int);
      if (field_width < 0)
      {
        field_width = -field_width;
        flags |= LEFT;
      }
    }

    /* get the precision */
    precision = -1;
    if (*fmt == '.')
    {
      ++fmt;
      if (isdigit(*fmt))
        precision = skip_atoi(&fmt);
      else if (*fmt == '*')
      {
        ++fmt;
        /* it's the next argument */
        precision = va_arg(args, int);
      }
      if (precision < 0)
        precision = 0;
    }

    /* get the conversion qualifier */
    qualifier = -1;
    if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' ||
        *fmt == 'Z' || *fmt == 'z')
    {
      qualifier = *fmt;
      ++fmt;
      if (qualifier == 'l' && *fmt == 'l')
      {
        qualifier = 'L';
        ++fmt;
      }
    }

    /* default base */
    base = 10;

    switch (*fmt)
    {

    case 'c':
      if (!(flags & LEFT))
      {
        while (--field_width > 0)
        {
          if (str <= end)
            *str = ' ';
          ++str;
        }
      }
      c = (unsigned char)va_arg(args, int);
      if (str <= end)
        *str = c;
      ++str;
      while (--field_width > 0)
      {
        if (str <= end)
          *str = ' ';
        ++str;
      }
      continue;

    case 's':
      s = va_arg(args, char *);
      if ((unsigned long)s < 1024) //PAGE_SIZE)
        s = "<NULL>";

      len = my_strnlen(s, precision);

      if (!(flags & LEFT))
      {
        while (len < field_width--)
        {
          if (str <= end)
            *str = ' ';
          ++str;
        }
      }
      for (i = 0; i < len; ++i)
      {
        if (str <= end)
          *str = *s;
        ++str;
        ++s;
      }
      while (len < field_width--)
      {
        if (str <= end)
          *str = ' ';
        ++str;
      }
      continue;

    case 'p':
      if (field_width == -1)
      {
        field_width = 2 * sizeof(void *);
        flags |= ZEROPAD;
      }
      str = number(str, end,
                   (unsigned long)va_arg(args, void *),
                   16, field_width, precision, flags);
      continue;

    case 'n':
      /* FIXME:
				* What does C99 say about the overflow case here? */
      if (qualifier == 'l')
      {
        long *ip = va_arg(args, long *);
        *ip = (str - buf);
      }
      else if (qualifier == 'Z' || qualifier == 'z')
      {
        size_t *ip = va_arg(args, size_t *);
        *ip = (str - buf);
      }
      else
      {
        int *ip = va_arg(args, int *);
        *ip = (str - buf);
      }
      continue;

    case '%':
      if (str <= end)
        *str = '%';
      ++str;
      continue;

      /* integer number formats - set up the flags and "break" */
    case 'o':
      base = 8;
      break;

    case 'X':
      flags |= LARGE;
    case 'x':
      base = 16;
      break;

    case 'd':
    case 'i':
      flags |= SIGN;
    case 'u':
      break;

    default:
      if (str <= end)
        *str = '%';
      ++str;
      if (*fmt)
      {
        if (str <= end)
          *str = *fmt;
        ++str;
      }
      else
      {
        --fmt;
      }
      continue;
    }

    if (qualifier == 'L')
      num = va_arg(args, unsigned long long);
    else if (qualifier == 'l')
    {
      num = va_arg(args, unsigned long);
      if (flags & SIGN)
        num = (signed long)num;
    }
    else if (qualifier == 'Z' || qualifier == 'z')
    {
      num = va_arg(args, size_t);
    }
    else if (qualifier == 'h')
    {
      num = (unsigned short)va_arg(args, int);
      if (flags & SIGN)
        num = (signed short)num;
    }
    else
    {
      num = va_arg(args, unsigned int);
      if (flags & SIGN)
        num = (signed int)num;
    }
    str = number(str, end, num, base,
                 field_width, precision, flags);
  }
  if (str <= end)
    *str = '\0';
  else if (size > 0)
    /* don't write out a null byte if the buf size is zero */
    *end = '\0';
  /* the trailing null byte doesn't count towards the total
	* ++str;
	*/
  return str - buf;
}

//EXPORT_SYMBOL(my_vsnprintf);

/**
 * vscnprintf - Format a string and place it in a buffer
 * @buf: The buffer to place the result into
 * @size: The size of the buffer, including the trailing null space
 * @fmt: The format string to use
 * @args: Arguments for the format string
 *
 * The return value is the number of characters which have been written into
 * the @buf not including the trailing '\0'. If @size is <= 0 the function
 * returns 0.
 *
 * Call this function if you are already dealing with a my_va_list.
 * You probably want scnprintf instead.
 */
int vscnprintf(char *buf, size_t size, const char *fmt, my_va_list args)
{
  int i;

  i = my_vsnprintf(buf, size, fmt, args);
  return (i >= size) ? (size - 1) : i;
}

//EXPORT_SYMBOL(vscnprintf);

/**
 * snprintf - Format a string and place it in a buffer
 * @buf: The buffer to place the result into
 * @size: The size of the buffer, including the trailing null space
 * @fmt: The format string to use
 * @...: Arguments for the format string
 *
 * The return value is the number of characters which would be
 * generated for the given input, excluding the trailing null,
 * as per ISO C99.  If the return is greater than or equal to
 * @size, the resulting string is truncated.
 */
int snprintf(char *buf, size_t size, const char *fmt, ...)
{
  my_va_list args;
  int i;

  va_start(args, fmt);
  i = my_vsnprintf(buf, size, fmt, args);
  va_end(args);
  return i;
}

//EXPORT_SYMBOL(snprintf);

/**
 * scnprintf - Format a string and place it in a buffer
 * @buf: The buffer to place the result into
 * @size: The size of the buffer, including the trailing null space
 * @fmt: The format string to use
 * @...: Arguments for the format string
 *
 * The return value is the number of characters written into @buf not including
 * the trailing '\0'. If @size is <= 0 the function returns 0. If the return is
 * greater than or equal to @size, the resulting string is truncated.
 */

int scnprintf(char *buf, size_t size, const char *fmt, ...)
{
  my_va_list args;
  int i;

  va_start(args, fmt);
  i = my_vsnprintf(buf, size, fmt, args);
  va_end(args);
  return (i >= size) ? (size - 1) : i;
}
//EXPORT_SYMBOL(scnprintf);

/**
 * my_vsprintf - Format a string and place it in a buffer
 * @buf: The buffer to place the result into
 * @fmt: The format string to use
 * @args: Arguments for the format string
 *
 * The function returns the number of characters written
 * into @buf. Use my_vsnprintf or vscnprintf in order to avoid
 * buffer overflows.
 *
 * Call this function if you are already dealing with a my_va_list.
 * You probably want sprintf instead.
 */
int my_vsprintf(char *buf, const char *fmt, my_va_list args)
{
  return my_vsnprintf(buf, (~0U) >> 1, fmt, args);
}

//EXPORT_SYMBOL(my_vsprintf);

/**
 * sprintf - Format a string and place it in a buffer
 * @buf: The buffer to place the result into
 * @fmt: The format string to use
 * @...: Arguments for the format string
 *
 * The function returns the number of characters written
 * into @buf. Use snprintf or scnprintf in order to avoid
 * buffer overflows.
 */
int sprintf(char *buf, const char *fmt, ...)
{
  my_va_list args;
  int i;

  va_start(args, fmt);
  /*	putc('P');
	putc(0x0d);
	putc(0x0a);
*/
  i = my_vsprintf(buf, fmt, args);
  /*	putc('E');
	putc(0x0d);
	putc(0x0a);
*/
  va_end(args);
  return i;
}

//EXPORT_SYMBOL(sprintf);

/**
 * my_vsscanf - Unformat a buffer into a list of arguments
 * @buf:	input buffer
 * @fmt:	format of buffer
 * @args:	arguments
 */
int my_vsscanf(const char *buf, const char *fmt, my_va_list args)
{
  const char *str = buf;
  char *next;
  char digit;
  int num = 0;
  int qualifier;
  int base;
  int field_width;
  int is_sign = 0;

  while (*fmt && *str)
  {
    /* skip any white space in format */
    /* white space in format matchs any amount of
		 * white space, including none, in the input.
		 */
    if (isspace(*fmt))
    {
      while (isspace(*fmt))
        ++fmt;
      while (isspace(*str))
        ++str;
    }

    /* anything that is not a conversion must match exactly */
    if (*fmt != '%' && *fmt)
    {
      if (*fmt++ != *str++)
        break;
      continue;
    }

    if (!*fmt)
      break;
    ++fmt;

    /* skip this conversion.
		 * advance both strings to next white space
		 */
    if (*fmt == '*')
    {
      while (!isspace(*fmt) && *fmt)
        fmt++;
      while (!isspace(*str) && *str)
        str++;
      continue;
    }

    /* get field width */
    field_width = -1;
    if (isdigit(*fmt))
      field_width = skip_atoi(&fmt);

    /* get conversion qualifier */
    qualifier = -1;
    if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' ||
        *fmt == 'Z' || *fmt == 'z')
    {
      qualifier = *fmt++;
      if (unlikely(qualifier == *fmt))
      {
        if (qualifier == 'h')
        {
          qualifier = 'H';
          fmt++;
        }
        else if (qualifier == 'l')
        {
          qualifier = 'L';
          fmt++;
        }
      }
    }
    base = 10;
    is_sign = 0;

    if (!*fmt || !*str)
      break;

    switch (*fmt++)
    {
    case 'c':
    {
      char *s = (char *)va_arg(args, char *);
      if (field_width == -1)
        field_width = 1;
      do
      {
        *s++ = *str++;
      } while (--field_width > 0 && *str);
      num++;
    }
      continue;
    case 's':
    {
      char *s = (char *)va_arg(args, char *);
      if (field_width == -1)
        field_width = INT_MAX;
      /* first, skip leading white space in buffer */
      while (isspace(*str))
        str++;

      /* now copy until next white space */
      while (*str && !isspace(*str) && field_width--)
      {
        *s++ = *str++;
      }
      *s = '\0';
      num++;
    }
      continue;
    case 'n':
      /* return number of characters read so far */
      {
        int *i = (int *)va_arg(args, int *);
        *i = str - buf;
      }
      continue;
    case 'o':
      base = 8;
      break;
    case 'x':
    case 'X':
      base = 16;
      break;
    case 'i':
      base = 0;
    case 'd':
      is_sign = 1;
    case 'u':
      break;
    case '%':
      /* looking for '%' in str */
      if (*str++ != '%')
        return num;
      continue;
    default:
      /* invalid format; stop here */
      return num;
    }

    /* have some sort of integer conversion.
		 * first, skip white space in buffer.
		 */
    while (isspace(*str))
      str++;

    digit = *str;
    if (is_sign && digit == '-')
      digit = *(str + 1);

    if (!digit || (base == 16 && !isxdigit(digit)) || (base == 10 && !isdigit(digit)) || (base == 8 && (!isdigit(digit) || digit > '7')) || (base == 0 && !isdigit(digit)))
      break;

    switch (qualifier)
    {
    case 'H': /* that's 'hh' in format */
      if (is_sign)
      {
        signed char *s = (signed char *)va_arg(args, signed char *);
        *s = (signed char)simple_strtol(str, &next, base);
      }
      else
      {
        unsigned char *s = (unsigned char *)va_arg(args, unsigned char *);
        *s = (unsigned char)simple_strtoul(str, &next, base);
      }
      break;
    case 'h':
      if (is_sign)
      {
        short *s = (short *)va_arg(args, short *);
        *s = (short)simple_strtol(str, &next, base);
      }
      else
      {
        unsigned short *s = (unsigned short *)va_arg(args, unsigned short *);
        *s = (unsigned short)simple_strtoul(str, &next, base);
      }
      break;
    case 'l':
      if (is_sign)
      {
        long *l = (long *)va_arg(args, long *);
        *l = simple_strtol(str, &next, base);
      }
      else
      {
        unsigned long *l = (unsigned long *)va_arg(args, unsigned long *);
        *l = simple_strtoul(str, &next, base);
      }
      break;
    case 'L':
      if (is_sign)
      {
        long long *l = (long long *)va_arg(args, long long *);
        *l = simple_strtoll(str, &next, base);
      }
      else
      {
        unsigned long long *l = (unsigned long long *)va_arg(args, unsigned long long *);
        *l = simple_strtoull(str, &next, base);
      }
      break;
    case 'Z':
    case 'z':
    {
      size_t *s = (size_t *)va_arg(args, size_t *);
      *s = (size_t)simple_strtoul(str, &next, base);
    }
    break;
    default:
      if (is_sign)
      {
        int *i = (int *)va_arg(args, int *);
        *i = (int)simple_strtol(str, &next, base);
      }
      else
      {
        unsigned int *i = (unsigned int *)va_arg(args, unsigned int *);
        *i = (unsigned int)simple_strtoul(str, &next, base);
      }
      break;
    }
    num++;

    if (!next)
      break;
    str = next;
  }
  return num;
}

//EXPORT_SYMBOL(my_vsscanf);

/**
 * sscanf - Unformat a buffer into a list of arguments
 * @buf:	input buffer
 * @fmt:	formatting of buffer
 * @...:	resulting arguments
 */
int sscanf(const char *buf, const char *fmt, ...)
{
  my_va_list args;
  int i;

  va_start(args, fmt);
  i = my_vsscanf(buf, fmt, args);
  va_end(args);
  return i;
}